home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / dte5_1.zip / HWIBM.C < prev    next >
C/C++ Source or Header  |  1991-02-06  |  30KB  |  988 lines

  1. /*
  2.  * Written by Douglas Thomson (1989/1990)
  3.  *
  4.  * This source code is released into the public domain.
  5.  */
  6.  
  7. /*
  8.  * Name:    dte - Doug's Text Editor program - hardware dependent module
  9.  * Purpose: This file contains all the code that needs to be different on
  10.  *           different hardware.
  11.  * File:    hwibm.c
  12.  * Author:  Douglas Thomson
  13.  * System:  This particular version is for the IBM PC and close compatibles.
  14.  *           It write directly to video RAM, so it is faster than other
  15.  *           techniques, but will cause "snow" on most CGA cards. See the
  16.  *           file "hwibmcga.c" for a version that avoids snow.
  17.  *          The compiler is Turbo C 2.0, using one of the large data memory
  18.  *           models.
  19.  * Date:    October 10, 1989
  20.  * Notes:   This module has been kept as small as possible, to facilitate
  21.  *           porting between different systems.
  22.  */
  23. #include "common.h"     /* dte types */
  24. #include "hwdep.h"      /* prototypes for functions here */
  25. #include "utils.h"      /* for displaying messages etc */
  26. #include "version.h"    /* current version number */
  27. #include <stdarg.h>     /* for passing variable numbers of arguments */
  28. #include <conio.h>      /* for using putch to output a character */
  29. #include <dos.h>        /* for renaming files */
  30. #include <dir.h>        /* for searching the current path */
  31. #include <bios.h>       /* for direct BIOS keyboard input */
  32. #include <alloc.h>      /* for memory allocation */
  33. #include <io.h>         /* for file attribute code */
  34. #include <fcntl.h>      /* open flags */
  35. #include <process.h>    /* spawn etc */
  36. #include <sys/stat.h>   /* S_IWRITE etc */
  37.  
  38. /*
  39.  * prototypes for all functions in this file
  40.  */
  41. void error ARGS((int kind, ...));
  42. void main ARGS((int argc, char *argv[]));
  43. void hw_xygoto ARGS((void));
  44. int hw_clreol ARGS((void));
  45. int hw_linedel ARGS((int line));
  46. int hw_scroll_up ARGS((int top, int bottom));
  47. int hw_lineins ARGS((int line));
  48. int hw_scroll_down ARGS((int top, int bottom));
  49. int hw_c_avail ARGS((void));
  50. int hw_c_input ARGS((void));
  51. void hw_c_output ARGS((int c));
  52. void hw_terminate ARGS((void));
  53. void hw_initialize ARGS((void));
  54. void hw_move ARGS((text_ptr dest, text_ptr source, long number));
  55. int hw_backspace ARGS((void));
  56. int hw_c_insert ARGS((void));
  57. int hw_c_delete ARGS((void));
  58. int hw_rename ARGS((char *old, char *new));
  59. int hw_fattrib ARGS((char *name));
  60. int hw_set_fattrib ARGS((char *name, int attrib));
  61. int hw_unlink ARGS((char *name));
  62. int hw_printable ARGS((int c));
  63. static int write_file ARGS((char *name, char *mode, text_ptr start,
  64.         text_ptr end));
  65. int hw_save ARGS((char *name, text_ptr start, text_ptr end));
  66. int hw_append ARGS((char *name, text_ptr start, text_ptr end));
  67. int hw_print ARGS((text_ptr start, text_ptr end));
  68. int hw_load ARGS((char *name, text_ptr start, text_ptr limit, text_ptr *end));
  69. void hw_copy_path ARGS((char *old, char *name, char *new));
  70. int hw_os_shell ARGS((void));
  71.  
  72. /*
  73.  * Name:    error
  74.  * Purpose: To report an error, and usually make the user type <ESC> before
  75.  *           continuing.
  76.  * Date:    October 10, 1989
  77.  * Passed:  kind:   an indication of how serious the error was:
  78.  *                      TEMP:    merely a message, do not wait for <ESC>
  79.  *                      DIAG:    merely a message, but make sure user sees it
  80.  *                      WARNING: error, but editor can continue after <ESC>
  81.  *                      FATAL:   abort the editor!
  82.  *          format: printf format string for any arguments that follow
  83.  *          ...:    arguments to be printed
  84.  * Notes:   This function should be system independent; that is the whole
  85.  *           point of the "stdarg" philosophy. However, two of the systems
  86.  *           I have used implemented "stdarg" incompatibly, and some older
  87.  *           systems may not support the "stdarg" macros at all...
  88.  */
  89. void error(kind, format)
  90. int kind;
  91. char *format;
  92. {
  93.     va_list argptr;         /* used to access various arguments */
  94.     char buff[MAX_COLS];    /* somewhere to store error before printing */
  95.     int c;                  /* character entered by user to continue */
  96.  
  97.     /*
  98.      * prepare to process variable arguments
  99.      */
  100.     va_start(argptr, format);
  101.  
  102.     /*
  103.      * tell the user what kind of an error it is
  104.      */
  105.     switch (kind) {
  106.     case FATAL:
  107.         strcpy(buff, "Fatal error: ");
  108.         break;
  109.     case WARNING:
  110.         strcpy(buff, "Warning: ");
  111.         break;
  112.     case DIAG:
  113.     case TEMP:
  114.         strcpy(buff, "");
  115.         break;
  116.     }
  117.  
  118.     /*
  119.      * prepare the error message itself
  120.      */
  121.     vsprintf(buff + strlen(buff), format, argptr);
  122.     va_end(argptr);
  123.  
  124.     /*
  125.      * tell the user how to continue editing if necessary
  126.      */
  127.     if (kind == WARNING || kind == DIAG) {
  128.         strcat(buff, ": type <ESC>");
  129.     }
  130.  
  131.     /*
  132.      * output the error message
  133.      */
  134.     set_prompt(buff, 1);
  135.  
  136.     if (kind == FATAL) {
  137.         /*
  138.          * no point in making the user type <ESC>, since the program is
  139.          *  about to abort anyway...
  140.          */
  141.         terminate();
  142.         exit(1);
  143.     }
  144.     else if (kind != TEMP) {
  145.         /*
  146.          * If necessary, force the user to acknowledge the error by
  147.          *  typing <ESC> (or ^U).
  148.          * This prevents any extra commands the user has entered from
  149.          *  causing problems after an error may have made them inappropriate.
  150.          */
  151.         while ((c=c_input()) != 27 && c != CONTROL('U')) {
  152.             set_prompt(buff, 1);
  153.         }
  154.     }
  155. }
  156.  
  157. /*
  158.  * Name:    harmless
  159.  * Purpose: To process control-break by ignoring it, so that the editor is
  160.  *           not aborted!
  161.  * Date:    February 5, 1990
  162.  */
  163. static int harmless(void)
  164. {
  165.     return 1;   /* ignore */
  166. }
  167.  
  168.  
  169. /*
  170.  * original control-break checking flag
  171.  */
  172. static int s_cbrk;
  173.  
  174. /*
  175.  * Name:    main
  176.  * Purpose: To do any system dependent command line argument processing,
  177.  *           and then call the main editor function.
  178.  * Date:    October 10, 1989
  179.  * Passed:  argc:   number of command line arguments
  180.  *          argv:   text of command line arguments
  181.  */
  182. void main(argc, argv)
  183. int argc;
  184. char *argv[];
  185. {
  186.     char drive[MAXDRIVE];  /* drive which dte.exe came from */
  187.     char dir[MAXDIR];      /* directory for dte.exe */
  188.  
  189.     /*
  190.      * trap control-break to make it harmless, and turn checking off
  191.      */
  192.     s_cbrk = getcbrk();
  193.     ctrlbrk(harmless);
  194.     setcbrk(0);
  195.  
  196.     /*
  197.      * set up help file name. This is a file called dte.hlp, and it should
  198.      *  be in the same directory as the dte.exe program.
  199.      * This information is only available in DOS 3 and later, so we need
  200.      *  to check to see whether argv[0] was OK.
  201.      */
  202.     if (fnsplit(argv[0], drive, dir, NULL, NULL) & DIRECTORY) {
  203. #ifdef GRIB
  204.         fnmerge(g_status.help_file, drive, dir, "dtegrib", ".hlp");
  205. #else
  206.         fnmerge(g_status.help_file, drive, dir, "dte", ".hlp");
  207. #endif
  208.     }
  209.     editor(argc, argv);
  210. }
  211.  
  212. /*
  213.  * The following defines specify which video attributes give desired
  214.  *  effects on different display devices.
  215.  * REVERSE is supposed to be reverse video - a different background color,
  216.  *  so that even a blank space can be identified.
  217.  * HIGH is supposed to quickly draw the user's eye to the relevant part of
  218.  *  the screen, either for a message or for matched text in find/replace.
  219.  * NORMAL is supposed to be something pleasant to look at for the main
  220.  *  body of the text.
  221.  * These defines may not be optimal for all types of display. Eventually
  222.  *  the user should be allowed to select which attribute is used where.
  223.  */
  224. #define LCD_REVERSE 0x70
  225. #define LCD_NORMAL  0x07
  226. #define LCD_HIGH    0x17
  227.  
  228. #define HERC_REVERSE 0x70
  229. #define HERC_UNDER   0x01
  230. #define HERC_NORMAL  0x07
  231. #define HERC_HIGH    0x0F
  232.  
  233. #define COLOR_NORMAL 0x07
  234. #define COLOR_REVERSE 0x17
  235. #define COLOR_HIGH 0x1F
  236.  
  237. /*
  238.  * Name:    hw_xygoto
  239.  * Purpose: To move the cursor to a new position on the screen.
  240.  * Date:    October 10, 1989
  241.  * Passed:  [g_display.line]: the required line
  242.  *          [g_display.col]:  the required column
  243.  */
  244. void hw_xygoto()
  245. {
  246.     gotoxy(g_display.col+1, g_display.line+1);
  247. }
  248.  
  249. /*
  250.  * Name:    hw_clreol
  251.  * Purpose: To clear from the cursor to the end of the cursor line.
  252.  * Date:    October 10, 1989
  253.  * Returns: TRUE if the hardware could clear to end of line, FALSE otherwise
  254.  */
  255. int hw_clreol()
  256. {
  257.     return TRUE;
  258. }
  259.  
  260. /*
  261.  * Name:    hw_linedel
  262.  * Purpose: To delete the cursor line, scrolling lines below up.
  263.  * Date:    October 10, 1989
  264.  * Passed:  line:  line on screen to be deleted
  265.  * Returns: TRUE if the hardware could delete the line, FALSE otherwise
  266.  */
  267. int hw_linedel(line)
  268. int line;
  269. {
  270.     return TRUE;
  271. }
  272.  
  273. /*
  274.  * Name:    hw_scroll_up
  275.  * Purpose: To scroll the lines in a given region up one line.
  276.  * Date:    October 10, 1989
  277.  * Passed:  top:    the top line in the window
  278.  *          bottom: the bottom line in the window
  279.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  280.  * Notes:   If this function does not exist, then insert and delete line
  281.  *           can achieve the same effect. However, insert and delete line
  282.  *           make lower windows jump, so using terminal scrolling is
  283.  *           preferable.
  284.  */
  285. int hw_scroll_up(top, bottom)
  286. int top;
  287. int bottom;
  288. {
  289.     return TRUE;
  290. }
  291.  
  292. /*
  293.  * Name:    hw_lineins
  294.  * Purpose: To insert a blank line above the cursor line, scrolling the
  295.  *           cursor line and lines below down.
  296.  * Date:    October 10, 1989
  297.  * Passed:  line:  line on screen to be inserted
  298.  * Returns: TRUE if the hardware could insert the line, FALSE otherwise
  299.  */
  300. int hw_lineins(line)
  301. int line;
  302. {
  303.     return TRUE;
  304. }
  305.  
  306. /*
  307.  * Name:    hw_scroll_down
  308.  * Purpose: To scroll the lines in a given region down one line.
  309.  * Date:    October 10, 1989
  310.  * Passed:  top:    the top line in the window
  311.  *          bottom: the bottom line in the window
  312.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  313.  * Notes:   If this function does not exist, then insert and delete line
  314.  *           can achieve the same effect. However, insert and delete line
  315.  *           make lower windows jump, so using terminal scrolling is
  316.  *           preferable.
  317.  */
  318. int hw_scroll_down(top, bottom)
  319. int top;
  320. int bottom;
  321. {
  322.     return TRUE;
  323. }
  324.  
  325. /*
  326.  * Name:    hw_c_avail
  327.  * Purpose: To test whether or not a character has been typed by the user.
  328.  * Date:    October 10, 1989
  329.  * Returns: TRUE if user typed something, FALSE otherwise
  330.  */
  331. int hw_c_avail()
  332. {
  333.     return bioskey(1);
  334. }
  335.  
  336. /*
  337.  * Name:    hw_c_input
  338.  * Purpose: To input a character from the user, without echo, waiting if
  339.  *           nothing has been typed yet.
  340.  * Date:    October 10, 1989
  341.  * Returns: the character the user typed
  342.  * Notes:   A return value of 0 means that what the user typed should be
  343.  *           ignored.
  344.  */
  345. int hw_c_input()
  346. {
  347.     int key;
  348.  
  349.     g_screen[0][MAX_COLS-2].c = ((g_display.col+1) / 10) + '0';
  350.     g_screen[0][MAX_COLS-1].c = ((g_display.col+1) % 10) + '0';
  351.  
  352.     key = bioskey(0);
  353.     if ((key & 0xFF) == 0) {
  354.         /*
  355.          * The user entered a function key. Translate it into the
  356.          *  appropriate command, or ignore.
  357.          */
  358.         if (key == 0x4700) { /* home */
  359.             c_uninput(CONTROL('S'));
  360.             return CONTROL('Q');
  361.         }
  362.         if (key == 0x4800) { /* up arrow */
  363.             return CONTROL('E');
  364.         }
  365.         if (key == 0x4900) { /* page up */
  366.             return CONTROL('R');
  367.         }
  368.         if (key == 0x4b00) { /* left arrow */
  369.             return CONTROL('S');
  370.         }
  371.         if (key == 0x4d00) { /* right arrow */
  372.             return CONTROL('D');
  373.         }
  374.         if (key == 0x4f00) { /* end */
  375.             c_uninput(CONTROL('D'));
  376.             return CONTROL('Q');
  377.         }
  378.         if (key == 0x5000) { /* down arrow */
  379.             return CONTROL('X');
  380.         }
  381.         if (key == 0x5100) { /* page down */
  382.             return CONTROL('C');
  383.         }
  384.         if (key == 0x5200) { /* insert */
  385.             return CONTROL('V');
  386.         }
  387.         if (key == 0x5300) { /* del */
  388.             return CONTROL('G');
  389.         }
  390.         if (key == 0x2d00) { /* AltX */
  391.             c_uninput(CONTROL('X'));
  392.             return CONTROL('K');
  393.         }
  394.         return 0;
  395.     }
  396.     else {
  397.         return key & 0xFF;
  398.     }
  399. }
  400.  
  401. /*
  402.  * Name:    hw_c_output
  403.  * Purpose: To output a character, using the current attribute, at the
  404.  *           current screen position.
  405.  * Date:    October 10, 1989
  406.  * Notes:   Although we do not need to actually output the character here,
  407.  *           we do need to advance the cursor for the user to see.
  408.  */
  409. void hw_c_output(c)
  410. int c;
  411. {
  412.     gotoxy(g_display.col+2, g_display.line+1);
  413.     return;
  414. }
  415.  
  416. /*
  417.  * Name:    hw_terminate
  418.  * Purpose: To restore the terminal to a safe state prior to leaving the
  419.  *           editor.
  420.  * Date:    October 10, 1989
  421.  */
  422. void hw_terminate()
  423. {
  424.     gotoxy(g_display.ncols, g_display.nlines);
  425.     textattr(g_display.normal);
  426.     putch(' ');
  427.     printf("dte version %s for IBM PC", VERSION);
  428.  
  429.     /*
  430.      * restore control-break checking
  431.      */
  432.     setcbrk(s_cbrk);
  433. }
  434.  
  435. /*
  436.  * Name:    hw_initialize
  437.  * Purpose: To initialize the display ready for editor use.
  438.  * Date:    October 10, 1989
  439.  */
  440. void hw_initialize()
  441. {
  442.     struct text_info buff; /* for discovering display type */
  443.     long space;            /* amount of memory to use */
  444.  
  445.     /*
  446.      * set up path name for help file
  447.      */
  448.     if (*g_status.help_file == '\0') {
  449.         strcpy(g_status.help_file, searchpath("dte.hlp"));
  450.     }
  451.  
  452.     /*
  453.      * set up screen size
  454.      */
  455.     g_display.ncols = MAX_COLS;
  456.     g_display.nlines = MAX_LINES;
  457.  
  458.     /*
  459.      * cursor addressing is very cheap with a memory mapped display!
  460.      */
  461.     g_display.ca_len = 0;
  462.  
  463.     /*
  464.      * use almost all the available memory for the text buffer, but
  465.      *  reserve some for opening files and windows and shelling to
  466.      *  DOS.
  467.      * If there is plenty of memory available, then try to preserve
  468.      *  command.com as well.
  469.      */
  470.     space = farcoreleft() - 50000L;
  471.     if (space < 100000L) {
  472.         space += 40000L;
  473.     }
  474.     if ((g_status.start_mem = farmalloc(space)) == NULL) {
  475.         error(FATAL, "out of memory???");
  476.     }
  477.     g_status.max_mem = g_status.start_mem + space;
  478.  
  479.     /*
  480.      * work out what kind of display is in use, and set attributes and
  481.      *  display address accordingly. Note that this will only work with
  482.      *  close IBM compatibles.
  483.      */
  484.     gettextinfo(&buff);
  485.     if (buff.currmode == MONO) {
  486.         g_screen = (screen_lines *) 0xB0000000L;
  487.         g_display.block = HERC_REVERSE;
  488.         g_display.normal = HERC_NORMAL;
  489.         g_display.flash = HERC_HIGH;
  490.         g_display.attr = HERC_NORMAL;
  491.     }
  492.     else {
  493.         g_screen = (screen_lines *) 0xB8000000L;
  494.         if (buff.currmode == BW80) {
  495.             /*
  496.              * There are probably some machines apart from ones with liquid
  497.              *  crystal displays which use BW80 mode, in which case these
  498.              *  attributes may not be appropriate.
  499.              */
  500.             g_display.block = LCD_REVERSE;
  501.             g_display.normal = LCD_NORMAL;
  502.             g_display.flash = LCD_HIGH;
  503.             g_display.attr = LCD_NORMAL;
  504.         }
  505.         else {
  506.             g_display.block = COLOR_REVERSE;
  507.             g_display.normal = COLOR_NORMAL;
  508.             g_display.flash = COLOR_HIGH;
  509.             g_display.attr = COLOR_NORMAL;
  510.         }
  511.     }
  512. }
  513.  
  514. /*
  515.  * Name:    hw_move
  516.  * Purpose: To move data from one place to another as efficiently as
  517.  *           possible.
  518.  * Date:    October 10, 1989
  519.  * Passed:  dest:   where to copy to
  520.  *          source: where to copy from
  521.  *          number: number of bytes to copy
  522.  * Notes:   moves may be (usually will be) overlapped
  523.  */
  524. void hw_move(dest, source, number)
  525. text_ptr dest;
  526. text_ptr source;
  527. long number;
  528. {
  529.     if (number < 0) {
  530.         /*
  531.          * this should never happen...
  532.          */
  533.         error(WARNING, "negative move - contact Douglas Thomson");
  534.     }
  535.     else if (source == dest) {
  536.         /*
  537.          * nothing to be done
  538.          */
  539.         ;
  540.     }
  541.     else if (source > dest) {
  542.         /*
  543.          * Turbo C provides a move that can handle overlapping moves,
  544.          *  but unfortunately it can only move up to 64K-1 bytes.
  545.          * Since I could not move 64K, I have only tried to move 32K.
  546.          */
  547.         while (number > 0x8000L) {
  548.             memmove((char *)dest, (char *)source, 0x8000);
  549.             number -= 0x8000L;
  550.             dest += 0x8000L;
  551.             source += 0x8000L;
  552.         }
  553.         /*
  554.          * now less than 32K is left, so finish off the move
  555.          */
  556.         memmove((char *)dest, (char *)source, (unsigned)number);
  557.     }
  558.     else {
  559.         source += number;
  560.         dest += number;
  561.         while (number > 0x8000L) {
  562.             source -= 0x8000L;
  563.             dest -= 0x8000L;
  564.             number -= 0x8000L;
  565.             memmove((char *)dest, (char *)source, 0x8000);
  566.         }
  567.         source -= number;
  568.         dest -= number;
  569.         memmove((char *)dest, (char *)source, (unsigned)number);
  570.     }
  571. }
  572.  
  573. /*
  574.  * Name:    hw_backspace
  575.  * Purpose: To move the cursor left one position.
  576.  * Date:    October 10, 1989
  577.  * Returns: TRUE if the hardware could backspace, FALSE otherwise
  578.  * Notes:   This function is used where deletion requires a backspace,
  579.  *           space, backspace. If the terminal can backspace, this may
  580.  *           be much faster than using cursor addressing.
  581.  */
  582. int hw_backspace()
  583. {
  584.     gotoxy(g_display.col, g_display.line+1);
  585.     return TRUE;
  586. }
  587.  
  588. /*
  589.  * Name:    hw_c_insert
  590.  * Purpose: To insert a blank character under the cursor.
  591.  * Date:    October 10, 1989
  592.  * Returns: TRUE if the hardware could insert the space, FALSE otherwise
  593.  * Notes:   This function is used where the user has just typed a character
  594.  *           in the middle of a line in insert mode. If it is available, it
  595.  *           saves having to redraw the entire remainder of the line.
  596.  *          No assumptions are made about the contents or attribute of the
  597.  *           inserted character.
  598.  */
  599. int hw_c_insert()
  600. {
  601.     return TRUE;
  602. }
  603.  
  604. /*
  605.  * Name:    hw_c_delete
  606.  * Purpose: To delete the character under the cursor.
  607.  * Date:    October 10, 1989
  608.  * Returns: TRUE if the hardware could delete the character, FALSE otherwise
  609.  * Notes:   This function is used where the user has deleted a character
  610.  *           in the middle of a line. If it is available, it saves having to
  611.  *           redraw the entire remainder of the line.
  612.  *          The rightmost character on the line after the delete is assumed
  613.  *           to be a space character with normal attribute.
  614.  */
  615. int hw_c_delete()
  616. {
  617.     return TRUE;
  618. }
  619.  
  620. /*
  621.  * Name:    hw_rename
  622.  * Purpose: To rename a disk file to a new name.
  623.  * Date:    October 10, 1989
  624.  * Passed:  old: current file name
  625.  *          new: new desired file name
  626.  * Returns: OK if rename succeeded, ERROR if any problem
  627.  */
  628. int hw_rename(old, new)
  629. char *old;
  630. char *new;
  631. {
  632.     return rename(old, new);
  633. }
  634.  
  635. /*
  636.  * Name:    hw_fattrib
  637.  * Purpose: To determine the current file attributes.
  638.  * Date:    October 17, 1989
  639.  * Passed:  name: name of file to be checked
  640.  * Returns: current read/write/execute etc attributes of the file, or
  641.  *          ERROR if file did not exist etc.
  642.  */
  643. int hw_fattrib(name)
  644. char *name;
  645. {
  646.     return _chmod(name, 0);
  647. }
  648.  
  649. /*
  650.  * Name:    hw_set_fattrib
  651.  * Purpose: To set the current file attributes.
  652.  * Date:    October 17, 1989
  653.  * Passed:  name:   name of file to be changed
  654.  *          attrib: the required attributes
  655.  * Returns: new read/write/execute etc attributes of the file, or
  656.  *          ERROR if file did not exist etc.
  657.  * Notes:   If "attrib" is ERROR, then do not change attributes.
  658.  */
  659. int hw_set_fattrib(name, attrib)
  660. char *name;
  661. int attrib;
  662. {
  663.     if (attrib == ERROR) {
  664.         return ERROR;
  665.     }
  666.     return _chmod(name, 1, attrib);
  667. }
  668.  
  669. /*
  670.  * Name:    hw_unlink
  671.  * Purpose: To delete a file, regardless of access modes.
  672.  * Date:    October 17, 1989
  673.  * Passed:  name:   name of file to be removed
  674.  * Returns: OK if file could be removed
  675.  *          ERROR otherwise
  676.  */
  677. int hw_unlink(name)
  678. char *name;
  679. {
  680.     int result;
  681.  
  682.     if ((result = _chmod(name, 0)) != -1 && (result & FA_RDONLY) != 0) {
  683.         /*
  684.          * file cannot be written
  685.          */
  686.         set_prompt("File is write protected! Overwrite anyway? (y/n): ", 1);
  687.         if (display(get_yn, 1) != A_YES) {
  688.             return ERROR;
  689.         }
  690.         if (_chmod(name, 1, 0) == ERROR) {
  691.             return ERROR;
  692.         }
  693.     }
  694.     return unlink(name);
  695. }
  696.  
  697. /*
  698.  * Name:    hw_printable
  699.  * Purpose: To determine whether or not a character is printable on the
  700.  *           current hardware.
  701.  * Date:    October 18, 1989
  702.  * Passed:  c: the character to be tested
  703.  * Returns: TRUE if c is a visible character, FALSE otherwise
  704.  * Notes:   This is hardware dependent so that machines like the IBM PC can
  705.  *           edit files containing graphics characters.
  706.  */
  707. int hw_printable(c)
  708. int c;
  709. {
  710.    return (c >= 32);
  711. }
  712.  
  713. /*
  714.  * Name:    write_file
  715.  * Purpose: To write text to a file, eliminating trailing space on the
  716.  *           way.
  717.  * Date:    November 11, 1989
  718.  * Passed:  name:  name of disk file or device
  719.  *          mode:  fopen flags to be used in open
  720.  *          start: first character in text buffer
  721.  *          end:   last character (+1) in text buffer
  722.  * Returns: OK, or ERROR if anything went wrong
  723.  * Notes:   Trailing space at the very end of the text is NOT removed,
  724.  *           so that a block write of a block of spaces will work.
  725.  *          No error messages are displayed here, so the caller must
  726.  *           both tell the user what is happening, and print an error
  727.  *           message if anything goes wrong.
  728.  *          This function is in the hardware dependent module because
  729.  *           some computers require non-standard open parameters...
  730.  */
  731. static int write_file(name, mode, start, end)
  732. char *name;
  733. char *mode;
  734. text_ptr start;
  735. text_ptr end;
  736. {
  737.     FILE *fp;       /* file to be written */
  738.     int spaces;     /* no. of space characters pending */
  739.     char c;         /* current character in file */
  740.  
  741.     /*
  742.      * create a new file, or truncate an old one
  743.      */
  744.     if ((fp = fopen(name, mode)) == NULL) {
  745.         return ERROR;
  746.     }
  747.  
  748.     /*
  749.      * save the file, eliminating trailing space
  750.      */
  751.     spaces = 0;
  752.     for (;;) {
  753.         if (start == end) {
  754.             break;
  755.         }
  756.         if ((c = *start++) == ' ') {
  757.             spaces++;   /* count them, maybe output later */
  758.             continue;
  759.         }
  760.  
  761.         if (c == '\n') {
  762.             spaces = 0; /* eliminate the trailing space */
  763.         }
  764.         else if (spaces) {
  765.             /*
  766.              * the spaces were NOT trailing, so output them now
  767.              */
  768.             do {
  769.                 if (putc(' ', fp) == ERROR) {
  770.                     fclose(fp);
  771.                     return ERROR;
  772.                 }
  773.             } while (--spaces);
  774.         }
  775.  
  776.         if (putc(c, fp) == ERROR) {
  777.             fclose(fp);
  778.             return ERROR;
  779.         }
  780.     }
  781.  
  782.     /*
  783.      * output any trailing space at end of file - this may be important
  784.      *  for block writes.
  785.      */
  786.     if (spaces) {
  787.         do {
  788.             if (putc(' ', fp) == ERROR) {
  789.                 fclose(fp);
  790.                 return ERROR;
  791.             }
  792.         } while (--spaces);
  793.     }
  794.  
  795.     return fclose(fp);
  796. }
  797.  
  798. /*
  799.  * Name:    hw_save
  800.  * Purpose: To save text to a file, eliminating trailing space on the
  801.  *           way.
  802.  * Date:    November 11, 1989
  803.  * Passed:  name:  name of disk file
  804.  *          start: first character in text buffer
  805.  *          end:   last character (+1) in text buffer
  806.  * Returns: OK, or ERROR if anything went wrong
  807.  * Notes:   Trailing space at the very end of the file is NOT removed,
  808.  *           so that a block write of a block of spaces will work.
  809.  *          No error messages are displayed here, so the caller must
  810.  *           both tell the user what is happening, and print an error
  811.  *           message if anything goes wrong.
  812.  *          This function is in the hardware dependent module because
  813.  *           some computers require non-standard open parameters...
  814.  */
  815. int hw_save(name, start, end)
  816. char *name;
  817. text_ptr start;
  818. text_ptr end;
  819. {
  820.     return write_file(name, "w", start, end);
  821. }
  822.  
  823. /*
  824.  * Name:    hw_append
  825.  * Purpose: To append text to a file.
  826.  * Date:    November 11, 1989
  827.  * Passed:  name:  name of disk file
  828.  *          start: first character in text buffer
  829.  *          end:   last character (+1) in text buffer
  830.  * Returns: OK, or ERROR if anything went wrong
  831.  * Notes:   No error messages are displayed here, so the caller must
  832.  *           both tell the user what is happening, and print an error
  833.  *           message if anything goes wrong.
  834.  *          This function is in the hardware dependent module because
  835.  *           some computers require non-standard open parameters...
  836.  */
  837. int hw_append(name, start, end)
  838. char *name;
  839. text_ptr start;
  840. text_ptr end;
  841. {
  842.     return write_file(name, "a", start, end);
  843. }
  844.  
  845. /*
  846.  * Name:    hw_print
  847.  * Purpose: To print text to a printer.
  848.  * Date:    November 11, 1989
  849.  * Passed:  start: first character in text buffer
  850.  *          end:   last character (+1) in text buffer
  851.  * Returns: OK, or ERROR if anything went wrong
  852.  * Notes:   This function is in the hardware dependent module because
  853.  *           some computers require non-standard open parameters...
  854.  */
  855. int hw_print(start, end)
  856. text_ptr start;
  857. text_ptr end;
  858. {
  859.     return write_file("PRN", "a", start, end);
  860. }
  861.  
  862. /*
  863.  * Name:    hw_load
  864.  * Purpose: To load a file into the text buffer.
  865.  * Date:    November 11, 1989
  866.  * Passed:  name:  name of disk file
  867.  *          start: first character in text buffer
  868.  *          limit: last available character in text buffer
  869.  *          end:   last character (+1) from the file
  870.  * Returns: OK, or ERROR if anything went wrong
  871.  * Notes:   All error messages are displayed here, so the caller should
  872.  *           neither tell the user what is happening, nor print an error
  873.  *           message if anything goes wrong.
  874.  *          This function is in the hardware dependent module because
  875.  *           some computers require non-standard open parameters...
  876.  */
  877. int hw_load(name, start, limit, end)
  878. char *name;
  879. text_ptr start;
  880. text_ptr limit;
  881. text_ptr *end;
  882. {
  883.     int fd;         /* file being read */
  884.     int length;     /* number of bytes actually read */
  885.  
  886.     /*
  887.      * try reading the file
  888.      */
  889.     if ((fd = open(name, O_RDONLY)) == ERROR) {
  890.         error(WARNING, "File '%s' not found", name);
  891.         return ERROR;
  892.     }
  893.  
  894.     /*
  895.      * tell the user what is happening
  896.      */
  897.     error(TEMP, "Reading file '%s'...", name);
  898.  
  899.     /*
  900.      * read the entire file, without going past end of buffer.
  901.      * Note that this means a file that is within 1K of the limit
  902.      *  will not be accepted.
  903.      */
  904.     limit -= 1024;
  905.     for (;;) {
  906.         if (start >= limit) {
  907.             error(WARNING, "file '%s' too big", name);
  908.             close(fd);
  909.             return ERROR;
  910.         }
  911.         if ((length = read(fd, (char *)start, 1024)) == ERROR) {
  912.             error(WARNING, "could not read file '%s'", name);
  913.             close(fd);
  914.             return ERROR;
  915.         }
  916.         start += length;
  917.         if (length == 0) {
  918.             /*
  919.              * we reached the end of file
  920.              */
  921.             break;
  922.         }
  923.     }
  924.  
  925.     /*
  926.      * close the file and report the final character in the buffer
  927.      */
  928.     close(fd);
  929.     *end = start;
  930.  
  931.     return OK;
  932. }
  933.  
  934. /*
  935.  * Name:    hw_copy_path
  936.  * Purpose: To create a new file path using most of an old path but
  937.  *           changing just the file name.
  938.  * Date:    November 8, 1989
  939.  * Passed:  old:   the file path to extract path info from
  940.  *          name:  the file name to add to the extracted path info
  941.  * Returns: new:   the new path
  942.  * Notes:   The file is located in the same place as the original, so
  943.  *           that related editor files stay in the same directory.
  944.  *          This function is hardware dependent because different characters
  945.  *           delimit directories on different systems.
  946.  */
  947. void hw_copy_path(old, name, new)
  948. char *old;
  949. char *name;
  950. char *new;
  951. {
  952.     char *cp;           /* cutoff point in old path */
  953.  
  954.     strcpy(new, old);
  955.     if ((cp = strrchr(new, '/')) != NULL ||
  956.             (cp = strrchr(new, '\\')) != NULL ||
  957.             (cp = strrchr(new, ':')) != NULL) {
  958.         ++cp;
  959.     }
  960.     else {
  961.         cp = new;
  962.     }
  963.  
  964.     strcpy(cp, name);
  965. }
  966.  
  967. /*
  968.  * Name:    hw_os_shell
  969.  * Purpose: To shell out of the editor into the operating system, in such a
  970.  *           way that editing may be resumed later.
  971.  * Date:    November 28, 1990
  972.  * Returns: TRUE if screen may have been clobbered, FALSE if screen OK.
  973.  */
  974. int hw_os_shell()
  975. {
  976.     static unsigned char ci[MAXPATH];
  977.     static unsigned char dos_prompt[80];
  978.     if (ci[0] == '\0') {
  979.         strcpy(ci, getenv("COMSPEC"));
  980.     }
  981.     if (dos_prompt[0] == '\0') {
  982.         sprintf(dos_prompt, "PROMPT=[DTE] %s", getenv("PROMPT"));
  983.     }
  984.     putenv(dos_prompt);
  985.     spawnl(P_WAIT, ci, ci, 0);
  986.     return TRUE;
  987. }
  988.